package java_thinking.unit_15._15_7;

import java.lang.reflect.Array;
import java.util.*;

/**
 * @Description :   即使是Class.getTypeParameters()也只是表示有泛型声明的类型参数，观察其输出的标识符，并没什么用
 *                  所以，在泛型代码内部，无法获得任何有关泛型参数类型的信息
 *                  因此List<Integer>和List<String>在运行时事实上是相同的类型，这两种形式都被擦除成它们的原生类型即List
 *
 *                  在泛型代码内部，无法获得任何有关泛型参数类型的信息，java泛型是使用擦除来实现的，所以在使用泛型时，
 *                  任何具体类型信息都被擦除了，泛型参数将擦除到它第一个边界，比如<T extends HashF>这里T擦除到了HasF；
 *
 *                  在基于擦除实现中，泛型类型被当做第二类类型处理，即不能再某些重要的上下文环境中使用的类型，
 *                  泛型类型只有在静态类型检查期间才出现，在此之后，程序中所有泛型类型都将被擦除，替换为他们的非泛型上界
 *                  ，例如List<T>这样的类型注解将被擦除为List，而普通的类型变量在未指定边界的情况下将被擦除为Object；
 *
 *                  擦除核心动机是它使得泛化的客户端可以用非泛化的类库来使用，反之亦然。这经常被称为“迁移兼容性”
 *                  因此，擦除主要的正当理由是从非泛化的代码到泛化的代码的转变过程，以及在不破坏现有类库的情况下，将泛型融入Java语言。
 *
 *                  因为擦除在方法体中移除了类型信息，所以在运行时的问题就是边界：即对象进入和离开方法的地点。这正是编译器在编译期
 *                  执行类型检查并插入转型代码的地点
 *
 *
 *
 * 擦除的代码是显著的。
 * @author: cww
 * @DateTime: 2019/4/3 20:02
 */
class Frob {}
class Fnorkel{}
class Quark<Q> {}
class Particle<POSITION,MOMENTUM> {}
public class LostInformation {
    public static void main(String[] args) {
        List<Frob> list = new ArrayList<>();
        Map<Frob,Fnorkel> map = new HashMap<>();
        Quark<Fnorkel> quark = new Quark<>();
        Particle<Long,Double> p = new Particle<>();
        System.out.println(Arrays.toString(list.getClass().getTypeParameters()));
        System.out.println(Arrays.toString(map.getClass().getTypeParameters()));
        System.out.println(Arrays.toString(quark.getClass().getTypeParameters()));
        System.out.println(Arrays.toString(p.getClass().getTypeParameters()));
    }
}


class ArrayMaker<T> {
    /**
     * 即使kind被存储为Class<T>，擦除也意味着他将被存储为Class，所以在Array.newInstance()
     * 实际上并未拥有kind所包含的类型信息，所以这里必须转型，会有警告
     */
    private Class<T> type;

    public ArrayMaker(Class<T> type) {
        this.type = type;
    }

    @SuppressWarnings("unchecked")
    T[] createArray(int size) {
        return (T[]) Array.newInstance(type, size);
    }

    List<T> createList() {
        return new ArrayList<T>();
    }

    /**
     * @param args
     */
    public static void main(String[] args) {

        ArrayMaker<String> am2 = new ArrayMaker<>(String.class);
        System.out.println(Arrays.asList(am2.createArray(10)));
        System.out.println(Arrays.asList(am2.createList()));
    }

}
